package com.nokia.bpmn.provider.rest;

import com.fasterxml.jackson.core.type.TypeReference;
import com.seed.bpmn.engine.common.basic.Executor;
import com.seed.bpmn.engine.common.util.JsonUtil;
import com.seed.bpmn.engine.common.util.ResultData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Date;
import java.util.List;

/**
 * <p>基础</p>
 *
 * @author cscz89@126.com
 */
public abstract class BaseService {

    protected static Logger LOGGER = LoggerFactory.getLogger(BaseService.class);

    protected static String BASE_URL = "/bpmn";

    @Value("${bpmn.rest.url}")
    private String BPMN_REST_URL;

    @Resource
    protected RestTemplate restTemplate;

    protected <T> T execute(Executor<T> executor) {

        try {
            return executor.execute();
        } catch (Exception ee) {
            LOGGER.error("流程引擎客户端运行异常：[{}]", ee.getMessage());
            return null;
        }
    }

    protected <T> T post(TypeReference<ResultData<T>> typeReference, List<Param> paramList,
                         String url, String... arguments) {

        return execute(new Executor<T>() {
            @Override
            public T execute() throws Exception {

                File file = null;
                try {
                    String restUrl = BPMN_REST_URL + formatUrl(url, arguments);
                    LOGGER.info("url:{}", restUrl);
                    MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
                    if (paramList != null) {
                        for (Param p : paramList) {
                            String key = p.getKey();
                            Object value = p.getValue();
                            if (value instanceof InputStream) {
                                file = new File("lct_" + new Date().getTime()+ ".zip");
                                InputStream is = (InputStream)value;
                                inputstreamToFile(is, file);
                                param.add(key, new FileSystemResource(file));
                            } else if(value instanceof List){
                                for(Object v : (List<Object>)value){
                                    param.add(key, v);
                                }
                            } else {
                                param.add(key, value);
                            }
                        }
                    }
                    HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(param);
                    ResponseEntity<String> response = restTemplate.exchange(restUrl,
                        HttpMethod.POST, httpEntity, String.class);
                    return convert(response, typeReference);
                } finally {
                    if (file != null) {
                        file.delete();
                    }
                }
            }
        });
    }

    protected <T> T get(TypeReference<ResultData<T>> typeReference, String url, String... arguments) {

        return execute(new Executor<T>() {
            @Override
            public T execute() throws Exception {

                String restUrl = BPMN_REST_URL + formatUrl(url, arguments);
                LOGGER.info("url:{}", restUrl);
                ResponseEntity<String> response = restTemplate.exchange(restUrl, HttpMethod.GET,
                    null, String.class);
                return convert(response, typeReference);
            }
        });
    }

    protected <T> T get(Class<T> cls, String url, String... arguments) {

        return execute(new Executor<T>() {
            @Override
            public T execute() throws Exception {

                String restUrl = BPMN_REST_URL + formatUrl(url, arguments);
                LOGGER.info("url:{}", restUrl);
                ResponseEntity<T> response = restTemplate.exchange(restUrl, HttpMethod.GET, null,
                    cls);
                return response.getBody();
            }
        });
    }

    private <T> T convert(ResponseEntity<String> response,
                          TypeReference<ResultData<T>> typeReference) {

        String responseBody = response.getBody();
        LOGGER.info("返回数据：{}", response.getBody());
        ResultData<T> resultData = JsonUtil.parse(responseBody, typeReference);
        if (resultData.getStat().equals(ResultData.STAT_SUCCESS)) {
            LOGGER.info("数据返回状态:成功");
            return resultData.getData();
        } else {
            LOGGER.info("数据返回状态:失败，message：{}", resultData.getMessage());
            throw new IllegalStateException(resultData.getMessage());
        }
    }

    private String formatUrl(String url, String... arguments) {

        return arguments != null ? MessageFormat.format(url, arguments) : url;
    }

    protected void inputstreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.flush();
            os.close();
            ins.close();
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
    }

    class Param {

        private String key;
        private Object value;

        public Param() {
        }

        public Param(String key, Object value) {
            this.key = key;
            this.value = value;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }
    }
}
